package com.ck.framework.aop;

import com.alibaba.fastjson.JSON;
import com.ck.common.bean.McApiResp;
import com.ck.common.bean.McApiUserAuth;
import com.ck.common.constant.Constants;
import com.ck.common.entity.SysOperLog;
import com.ck.common.exception.BusinessException;
import com.ck.common.utils.ip.IpUtils;
import com.ck.common.utils.ip.ServletUtils;
import com.ck.common.utils.jwt.RedisCache;
import com.ck.common.utils.security.RsaUtil;
import com.ck.common.utils.security.sign.CtAes;
import com.ck.common.utils.txt.TextUtil;
import com.ck.common.vo.ReqVo;
import com.ck.common.vo.LoginUserVo;
import com.ck.system.service.AuthService;
import com.ck.system.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.NamedThreadLocal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author ck
 * @date 2024/1/23 14:32
 * desc: 操作日志记录
 */


/**
 * 业务层接口，会在此切面进行加解密操作
 */
@Aspect
@Slf4j
@Component
public class RsaLogOperationAspect {
    private static String isDebug;

    @Value("${isDebug}")
    public void setProfile(String pro) {
        isDebug = pro;
    }

    @Autowired
    AuthService authService;
    @Autowired
    RedisCache redisCache;

    @Autowired
    SysOperLogService sysOperLogService;

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");

    @Pointcut("execution(* com.ck.server.controller.test.login.rsa.business..*(..))")
    public void operationPointCut() {
    }

    @Around(value = "operationPointCut()",argNames = "point")
    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        TIME_THREADLOCAL.set(System.currentTimeMillis());
        SysOperLog operaLogEntity = new SysOperLog();

        String uri = ServletUtils.getRequest().getRequestURI();
        String keyToken = ServletUtils.getRequest().getHeader("keyToken");
        String username = null;
        Long id = null;
        log.info("uri:" + uri);

        //获取当前登录的用户信息
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof LoginUserVo) {
            LoginUserVo loginUser = (LoginUserVo) principal;
            id = loginUser.getUser().getUserId();
            username = loginUser.getUsername();
        }

        List<Object> args = Arrays.asList(point.getArgs());
        ArrayList<ReqVo> requestData = new ArrayList<>();
        Long finalId = id;
        args.forEach(obj -> {
            if (obj instanceof ReqVo) {
                ReqVo reqVo = (ReqVo) obj;
                try {
                    String decryptReqData = RsaUtil.decryptReqData(reqVo.getD(), keyToken, redisCache , finalId);
                    reqVo.setD(decryptReqData);
                    obj = reqVo;
                    operaLogEntity.setOperParam(JSON.toJSONString(reqVo));
                } catch (Exception e) {
                    log.info("业务层解密错误:{}", TextUtil.ex2Txt(e));
                    operaLogEntity.setErrorMsg(e.getMessage());
                }
                log.info("Params:" + JSON.toJSONString(obj));
            }
            requestData.add((ReqVo)obj);
        });

        Object proceed = point.proceed(requestData.toArray());
        String ipAddr = IpUtils.getIpAddr();
        // 设置方法名称
        String className = point.getTarget().getClass().getName();
        String methodName = point.getSignature().getName();
        operaLogEntity.setMethod(className + "." + methodName + "()");

        operaLogEntity.setOperIp(ipAddr);
        operaLogEntity.setRequestMethod(ServletUtils.getRequest().getMethod());
        operaLogEntity.setJsonResult(JSON.toJSONString(proceed));
        operaLogEntity.setStatus(Integer.parseInt(Constants.SUCCESS));
        operaLogEntity.setOperUrl(uri);
        operaLogEntity.setOperName(username);
        // 设置消耗时间
        operaLogEntity.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());

        log.info("response:{}", operaLogEntity.getJsonResult());
        sysOperLogService.insertOperlog(operaLogEntity);

        //加密
        if (!"Y".startsWith(isDebug)) {
            if (proceed instanceof McApiResp) {
                McApiResp mcApiResp = (McApiResp) proceed;
                if (mcApiResp.getCode().equals(Constants.SUCCESS)) {
                    try {
                        Map<String, Object> retMap = (Map<String, Object>) mcApiResp.getRetData();
                        McApiUserAuth mcApiUserAuth = authService.getUserAuthMapInRedisCache(keyToken, id);
                        mcApiResp.setRetData(new CtAes(mcApiUserAuth.getAesInfo().getVipara(),mcApiUserAuth.getAesInfo().getAesKey()).encrypt(JSON.toJSONString(retMap)));
                        proceed = mcApiResp;
                        return proceed;
                    } catch (Exception e) {
                        log.error("业务层加密错误:{}", TextUtil.ex2Txt(e));
                    }
                }
                return proceed;
            }
        }
        return proceed;
    }


    @AfterThrowing(value = "operationPointCut()",throwing = "ex")
    public void doErrorLog(JoinPoint joinPoint, Exception ex) {
        SysOperLog operaLogEntity = handleLog(joinPoint);
        if (null != ex){
            if (ex instanceof BusinessException || ex instanceof IllegalArgumentException) {
                operaLogEntity.setStatus(Integer.parseInt(Constants.SUCCESS));
                operaLogEntity.setErrorMsg(ex.getMessage());
                log.warn("message:{}", ex.getMessage());
            } else {
                operaLogEntity.setStatus(Integer.parseInt(Constants.FAIL));
                operaLogEntity.setErrorMsg(ex.getMessage());
                log.error("服务器错误:{}", ex.getMessage());
            }
        }
        log.info(operaLogEntity.toString());
        sysOperLogService.insertOperlog(operaLogEntity);
    }

    private SysOperLog handleLog(JoinPoint joinPoint) {

        SysOperLog operLogEntity = new SysOperLog();
        String username = null;
        //获取当前登录的用户信息
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof LoginUserVo) {
            LoginUserVo loginUser = (LoginUserVo) principal;
            username = loginUser.getUsername();
        }
        String uri = ServletUtils.getRequest().getRequestURI();
        ArrayList<ReqVo> list = new ArrayList<>();

        Arrays.asList(joinPoint.getArgs()).forEach(obj -> {
            if (obj instanceof ReqVo){
                list.add((ReqVo)obj);
            }
        });

        operLogEntity.setOperUrl(uri);
        operLogEntity.setOperIp(IpUtils.getIpAddr());
        operLogEntity.setRequestMethod(ServletUtils.getRequest().getMethod());

        // 设置方法名称
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        operLogEntity.setMethod(className + "." + methodName + "()");

        operLogEntity.setOperName(username);

        if (!list.isEmpty()){
            operLogEntity.setOperParam(JSON.toJSONString(list));
        }
        else {
            operLogEntity.setOperParam(null);
        }
        return operLogEntity;
    }
}
